#pragma once


#include <iostream>
#include <string>
#include <memory>
#include <sys/epoll.h>
#include "Log.hpp"
#include "Socket.hpp"



using namespace SocketModule;
using namespace LogModule;


const int gdefaultfd = -1;
#define MAX 4096


//最开始tcpserver只有一个listensockfe
class EpollServer
{
    static const int revs_num = 64;
public:
    EpollServer(int port)
    :_port(port),
    _epfd(gdefaultfd),
    _listen_socket(std::make_unique<TcpSocket>()),
    _isrunning(false)
    {

    }

    void Init()
    {
        _listen_socket->BuildTcpSocketMethod(_port);
        //1.创建epoll模型
        _epfd = epoll_create(256);
        // 这里数字随便写
        if(_epfd < 0)
        {
            LOG(LogLevel::ERROR) << "epoll_create error";
            exit(EPOLL_CREATE_ERR);
        }
        LOG(LogLevel::DEBUG) << "epoll_create success: " << _epfd;
        //2.至少要将listensock添加到epoll模型中,构建红黑树节点
        //所以事先存在一个监听的事件
        struct epoll_event ev;
        ev.events = EPOLLIN;
        ev.data.fd = _listen_socket->Fd();
        int n = epoll_ctl(_epfd,EPOLL_CTL_ADD,_listen_socket->Fd(),&ev);
        if(n < 0)
        {
            LOG(LogLevel::ERROR) << "epoll_ctl error";
            exit(EPOLL_CTL_ERR);
        }
    }

    void Loop()
    {
        int timeout = -1;
        _isrunning = true;
        while(_isrunning)
        {
            int n = epoll_wait(_epfd,_revs,revs_num,timeout);
            //检测那些事件就绪了,就绪事件存储在_revs中,一共n个事件就绪了
            switch(n)
            {
                case 0:
                    std::cout << "time out..." << std::endl;
                    break;
                case -1:
                    perror("select");
                    break;
                default:
                    Dispatcher(n);
                    break;
            }
        }
        _isrunning = false;
    }


    void Accepter()
    {
        //listenscokfd就绪了! 获取新链接不就好了吗?
        InetAddr client;
        int newfd = _listen_socket->Accepter(&client);//这里accepter时就不会阻塞了,select已经告诉我了,只拷贝就行了
        if (newfd < 0)
        {
            return;
        }
        else
        {
            std::cout << "获得了一个新链接: " << newfd << " client info: " << client.Addr() << std::endl;
            //我们要将这个新的newfd添加给epoll,让epoll来帮我们监管
            struct epoll_event ev;
            ev.events = EPOLLIN;
            ev.data.fd = newfd;
            int n = epoll_ctl(_epfd,EPOLL_CTL_ADD,newfd,&ev);
            if(n < 0)
            {
                LOG(LogLevel::ERROR) << "epoll_ctl error";
                close(newfd);
            }
            LOG(LogLevel::DEBUG) << "epoll_ctl success: " << newfd;
        }
    }
    void Recver(int fd)
    {
        //合法的就绪的,普通的fd
        //要根据协议进行读取
        char buffer[1024];
        //你怎么保证,你把一个或多个完整报文,全部都读完了?
        //我们要把历史读到的数据进行缓存
        ssize_t n = recv(fd,buffer,sizeof(buffer)-1,0);//这里的recv不会阻塞,select已经告诉我们他已经就绪了
        if(n > 0)
        {
            buffer[n] = 0;
            std::cout << "client# " << buffer << std::endl;
            std::string message = "echo# ";
            message += buffer;
            send(fd,message.c_str(),message.size(),0);
        }
        else if(n == 0)
        {
            LOG(LogLevel::DEBUG) << "客户端退出, sockfd: "<<fd;
            //要先从中移除,移完之后再关闭
            int m = epoll_ctl(_epfd,EPOLL_CTL_DEL,fd,nullptr);
            //将fd从epoll事件中删除,必须保证fd是一个合法的fd
            if(m < 0)
            {
                LOG(LogLevel::ERROR) << "epoll_ctl_del error";
                return;
            }
            LOG(LogLevel::DEBUG) << "epoll_ctl_del success: " << fd;
            close(fd);
        }
        else//读取失败
        {
            LOG(LogLevel::DEBUG) << "客户端读取出错, sockfd: "<<fd;
            
            //要先从中移除,移完之后再关闭
            int m = epoll_ctl(_epfd,EPOLL_CTL_DEL,fd,nullptr);
            //将fd从epoll事件中删除
            if(m < 0)
            {
                LOG(LogLevel::ERROR) << "epoll_ctl_del error";
                return;
            }
            LOG(LogLevel::DEBUG) << "epoll_ctl_del success: " << fd;
            close(fd);
        }
    }

    void Dispatcher(int rnum)//rfds就可能会有更多的fd就绪了,就不仅仅是listenfd就绪了
    {
        for(int i=0;i<rnum;i++)
        {
           int events = _revs[i].events;
           int fd = _revs[i].data.fd;
           if(fd == _listen_socket->Fd())
           {
                if(events & EPOLLIN)
                {
                    //listen sock fd 就绪
                    Accepter();
                }
           }
           else
           {
                //普通文件描述符就绪
                if(events & EPOLLIN)
                {
                    //读事件就绪
                    Recver(fd);
                }
                // else if(events & EPOLLOUT)
                // {
                //     //写事件就绪
                // }
           }
        }
    }


    ~EpollServer()
    {
        _listen_socket->Close();
        if(_epfd >= 0)
        {
            close(_epfd);
        }
    }
private:
    uint16_t _port;
    std::unique_ptr<Socket> _listen_socket;
    bool _isrunning;
    int _epfd;
    struct epoll_event _revs[revs_num];
};



